#include <util/Matrix.h>
#include <gtest/gtest.h>

using namespace util;

TEST(TestMatrix, testCtor00)
{
    Matrix m(0,0);

    ASSERT_EQ(0, m.rows());
    ASSERT_EQ(0, m.cols());
}

TEST(TestMatrix, testCtor11)
{
    Matrix m(1,1);

    ASSERT_EQ(1, m.rows());
    ASSERT_EQ(1, m.cols());

    ASSERT_EQ(0, m.getValue(0,0));
}

TEST(TestMatrix, testCtor23)
{
    Matrix m(2,3);

    ASSERT_EQ(2, m.rows());
    ASSERT_EQ(3, m.cols());
    ASSERT_EQ(0, m.getValue(1,2));
}

TEST(TestMatrix, testCopyCtor)
{
  Matrix m(2, 3);
  m(0,0) = 0.5; m(0,1) = 2; m(0,2) = 3;
  m(1,0) = 1; m(1,1) = -3; m(1,2) = 4;
  Matrix m2(m);
  ASSERT_EQ(m.rows(), m2.rows());
  ASSERT_EQ(m.cols(), m2.cols());
  ASSERT_DOUBLE_EQ(m(0,0), m2(0,0));
  ASSERT_DOUBLE_EQ(m(0,1), m2(0,1));
  ASSERT_DOUBLE_EQ(m(0,2), m2(0,2));
  ASSERT_DOUBLE_EQ(m(1,0), m2(1,0));
  ASSERT_DOUBLE_EQ(m(1,1), m2(1,1));
  ASSERT_DOUBLE_EQ(m(1,2), m2(1,2));
}

TEST(TestMatrix, testSetValue)
{
    Matrix m(2,3);
    m.setValue(0,0,1.0);
    ASSERT_EQ(1.0, m.getValue(0,0));
    m.setValue(1,2,3.0);
    ASSERT_EQ(3.0, m.getValue(1,2));
}

TEST(TestMatrix, testDiagonal)
{
  std::vector<Real> diag(2);
  diag[0] = 1.0;
  diag[1] = 2.0;
  Matrix m(2,diag,Matrix::DiagonalType());
  ASSERT_TRUE(m.isDiagonal());
  ASSERT_FALSE(m.isSimmetric());
  ASSERT_FALSE(m.isSparse());
  ASSERT_FALSE(m.isGeneric());

  ASSERT_EQ(1.0, m.getValue(0,0));
  ASSERT_EQ(0.0, m.getValue(0,1));
  ASSERT_EQ(0.0, m.getValue(1,0));
  ASSERT_EQ(2.0, m.getValue(1,1));
}

TEST(TestMatrix, testSimmetric)
{
  Matrix m(3, Matrix::SimmetricType());
  ASSERT_TRUE(m.isSimmetric());
  ASSERT_FALSE(m.isSparse());
  ASSERT_FALSE(m.isDiagonal());
  ASSERT_FALSE(m.isGeneric());
  m(0,0) = 1;
  m(0,1) = 2;

  ASSERT_DOUBLE_EQ(1, m(0,0));
  ASSERT_DOUBLE_EQ(2, m(0,1));
  ASSERT_DOUBLE_EQ(2, m(1,0));
  m(2,0) = 3;
  ASSERT_DOUBLE_EQ(3, m(0,2));
  ASSERT_DOUBLE_EQ(3, m(2,0));
}

TEST(TestMatrix, TestSparse)
{
  Matrix m(3, 5, Matrix::SparseType());
  ASSERT_TRUE(m.isSparse());
  ASSERT_FALSE(m.isDiagonal());
  ASSERT_FALSE(m.isGeneric());
  ASSERT_FALSE(m.isSimmetric());
  
  m(0,0) = 1;
  ASSERT_DOUBLE_EQ(1, m(0,0));
  m(0,1) = 2;
  ASSERT_DOUBLE_EQ(2, m(0,1));
  m(1,0) = 3;
  ASSERT_DOUBLE_EQ(3, m(1,0));
  m(2,0) = 4;
  ASSERT_DOUBLE_EQ(4, m(2,0));

  ASSERT_DOUBLE_EQ(0, m(2,2));
}

TEST(TestMatrix, testSumScalar)
{
  // 0.5 2 3
  // 1  -3 4
  Matrix m(2, 3);
  m(0,0) = 0.5; m(0,1) = 2; m(0,2) = 3;
  m(1,0) = 1; m(1,1) = -3; m(1,2) = 4;

  m += 4;
  ASSERT_DOUBLE_EQ(0.5 + 4, m(0,0));
  ASSERT_DOUBLE_EQ(2 + 4, m(0,1));
  ASSERT_DOUBLE_EQ(3 + 4, m(0,2));
  ASSERT_DOUBLE_EQ(1 + 4, m(1,0));
  ASSERT_DOUBLE_EQ(-3 + 4, m(1,1));
  ASSERT_DOUBLE_EQ(4 + 4, m(1,2));

  Matrix mSum = m + 5;
  ASSERT_DOUBLE_EQ(0.5 + 4 + 5, mSum(0,0));
  ASSERT_DOUBLE_EQ(2 + 4 + 5, mSum(0,1));
  ASSERT_DOUBLE_EQ(3 + 4 + 5, mSum(0,2));
  ASSERT_DOUBLE_EQ(1 + 4 + 5, mSum(1,0));
  ASSERT_DOUBLE_EQ(-3 + 4 + 5, mSum(1,1));
  ASSERT_DOUBLE_EQ(4 + 4 + 5, mSum(1,2));
}

TEST(TestMatrix, testDiffScalar)
{
  // 0.5 2 3
  // 1  -3 4
  Matrix m(2, 3);
  m(0,0) = 0.5; m(0,1) = 2; m(0,2) = 3;
  m(1,0) = 1; m(1,1) = -3; m(1,2) = 4;

  m -= 4;
  ASSERT_DOUBLE_EQ(0.5 - 4, m(0,0));
  ASSERT_DOUBLE_EQ(2 - 4, m(0,1));
  ASSERT_DOUBLE_EQ(3 - 4, m(0,2));
  ASSERT_DOUBLE_EQ(1 - 4, m(1,0));
  ASSERT_DOUBLE_EQ(-3 - 4, m(1,1));
  ASSERT_DOUBLE_EQ(4 - 4, m(1,2));

  Matrix mDiff = m - 5;
  ASSERT_DOUBLE_EQ(0.5 - 4 - 5, mDiff(0,0));
  ASSERT_DOUBLE_EQ(2 - 4 - 5, mDiff(0,1));
  ASSERT_DOUBLE_EQ(3 - 4 - 5, mDiff(0,2));
  ASSERT_DOUBLE_EQ(1 - 4 - 5, mDiff(1,0));
  ASSERT_DOUBLE_EQ(-3 - 4 - 5, mDiff(1,1));
  ASSERT_DOUBLE_EQ(4 - 4 - 5, mDiff(1,2));
}

TEST(TestMatrix, testTranspose)
{
  Matrix m(3, 3);
  m(0,0) = 0.5; m(0,1) = 2; m(0,2) = 3;
  m(1,0) = 1; m(1,1) = -3; m(1,2) = 4;
  m(2,0) = 0; m(2,1) = 3; m(2,2) = -4;

  Matrix transp = m.transposed();
//  MatrixView view(m);
//  view.toStream(std::cout);
//  MatrixView view2(transp);
//  view2.toStream(std::cout);
//  Formatter::getInstance().toStream(m, std::cout);
  ASSERT_EQ(m.rows(), transp.cols());
  ASSERT_EQ(m.cols(), transp.rows());

  ASSERT_DOUBLE_EQ(m(0,0), transp(0,0));
  ASSERT_DOUBLE_EQ(m(1,0), transp(0,1));
  ASSERT_DOUBLE_EQ(m(2,0), transp(0,2));
  ASSERT_DOUBLE_EQ(m(0,1), transp(1,0));
  ASSERT_DOUBLE_EQ(m(1,1), transp(1,1));
  ASSERT_DOUBLE_EQ(m(2,1), transp(1,2));
  ASSERT_DOUBLE_EQ(m(0,2), transp(2,0));
  ASSERT_DOUBLE_EQ(m(1,2), transp(2,1));
  ASSERT_DOUBLE_EQ(m(2,2), transp(2,2));
}

TEST(TestMatrix, testSimmetricAlgo)
{
  std::size_t size_ = 5;

  std::vector<int> values_(15);
  values_[0] = 0;  values_[1]  =  1;  values_[2] =  2;  values_[3] =  3;  values_[4] =  4;
                  values_[5]  = 11;  values_[6] = 12;  values_[7] = 13;  values_[8] = 14;
                                    values_[9] = 22;  values_[10] = 23; values_[11] = 24;
                                                     values_[12] = 33; values_[13] = 34;
                                                                      values_[14] = 44;

#define FUNC(row,column) row < column ? (values_.size() - ((size_ - row)*(size_ - row + 1))/2) + column - row : (values_.size() - ((size_ - column)*(size_ - column + 1))/2) + row - column

  ASSERT_EQ(0, FUNC(0,0));
  ASSERT_EQ(1, FUNC(0,1));
  ASSERT_EQ(2, FUNC(0,2));
  ASSERT_EQ(3, FUNC(0,3));
  ASSERT_EQ(4, FUNC(0,4));
  ASSERT_EQ(5, FUNC(1,1));
  ASSERT_EQ(6, FUNC(1,2));
  ASSERT_EQ(7, FUNC(1,3));
  ASSERT_EQ(8, FUNC(1,4));
  ASSERT_EQ(9, FUNC(2,2));
  ASSERT_EQ(10, FUNC(2,3));
  ASSERT_EQ(11, FUNC(2,4));
  ASSERT_EQ(12, FUNC(3,3));
  ASSERT_EQ(13, FUNC(3,4));
  ASSERT_EQ(14, FUNC(4,4));

  ASSERT_EQ(1, FUNC(1,0));
  ASSERT_EQ(2, FUNC(2,0));
  ASSERT_EQ(3, FUNC(3,0));
  ASSERT_EQ(4, FUNC(4,0));
  ASSERT_EQ(6, FUNC(2,1));
  ASSERT_EQ(7, FUNC(3,1));
  ASSERT_EQ(8, FUNC(4,1));
  ASSERT_EQ(10, FUNC(3,2));
  ASSERT_EQ(11, FUNC(4,2));
  ASSERT_EQ(13, FUNC(4,3));
}
